package app

import (
	"context"
	"golang.org/x/sync/errgroup"
	"log"
	"os"
	"os/signal"
	"syscall"
)

type Server interface {
	Start(ctx context.Context) error
	Stop(ctx context.Context) error
}

type App struct {
	ctx     context.Context
	cancel  context.CancelFunc
	options option
	servers []Server
}

func NewApp(options ...Option) App {
	opt := option{Name: "default"}

	for _, o := range options {
		o(&opt)
	}
	ctx, cancel := context.WithCancel(context.Background())
	return App{
		options: opt,
		ctx:     ctx,
		cancel:  cancel,
		servers: opt.Servers,
	}
}

func (a *App) Run() {
	eg, ctx := errgroup.WithContext(a.ctx)
	for _, s := range a.servers {
		eg.Go(func() error {
			return s.Start(ctx)
		})
		eg.Go(func() error {
			<-ctx.Done()
			return s.Stop(ctx)
		})
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
	eg.Go(func() error {
		for {
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-c:
				return a.Stop()
			}
		}
	})
	if err := eg.Wait(); err != nil {
		log.Fatal(err)
	}
}

func (a *App) Stop() error {
	a.cancel()
	return nil
}
